//////////////////////////////////////////////////////////////////////////////////
//	 Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <wx/wx.h>
#include "../common/MarkupSTL.h"
#include "logicmanager.h"
#include "adslprogmodem.h"

#define DOWN_STREAM_BIN_STARTS 31

CADSLProgModem::MODESELECTOR CADSLProgModem::gModes[]= {
{	"a", wxT("Multi Mode"),	   _("Auto"),										0,			0		 },
{	"d", wxT("G.DMT"),		   wxT("ADSL"),										12*1024,	1.3*1024 },
{	"l", wxT("G.Lite"),		   _("ADSL, Provides greater resistance to noise"),	1.5*1024,	128		 },
{	"t", wxT("T1.413"),			wxT("ADSL"),										15*1024,	1.5*1024 },
{	"2", wxT("ADSL2"),			 _("ADSL2 G.992.3"),								12*1024,	3.5*1024 },
{	"p", wxT("ADSL2+"),		   _("ADSL2 G.992.5"),								24*1024,	1.4*1024 },
{	"e", wxT("ADSL2, Annex L)"), _("ADSL2 Annex L, Reach extended"),				24*1024,	1.4*1024 },
{	"m", wxT("ADSL2+, Annex M"), _("ADSL2 Annex M, Double upstream"),				24*1024,	3*1024	 },
};

CADSLProgModem::CADSLProgModem(CLinkGuard& link, const wxString& strCMD):CADSLModem(link), m_strCMD(strCMD)
{
}

CADSLProgModem::~CADSLProgModem(void)
{
}

std::string CADSLProgModem::GetModemStats(std::string& strRawData)
{
	std::string strRet, strData;

	strData = m_link->SendCmd(m_strCMD+wxT(" info --stats"), 10000 );

	if ( !strData.empty() )
	{
		strRawData = strData;

		CMarkupSTL	doc;
		doc.AddElem( "adsl" );

		NAMEDVALS lst;

		if ( CLogicManager::ExtractPairs(":", strData, lst) )
		{
			doc.AddChildElem( ADSL_STAT_TAG  );

			for( size_t i=0;i<lst.size();i++ )
			{
				std::string strName = lst[i].strLabel;
				std::string strVal  = lst[i].strVal;

				unsigned long long lVals[10];

				if ( 0==strName.find("Link Power State") )
				{
					doc.AddChildAttrib(	ADSL_LINK_POWER_STATE_ATTR,  strVal.c_str() );
				}

				if ( 0==strName.find("Mode") )
				{
					doc.AddChildAttrib(	ADSL_MODE_ATTR,  strVal.c_str() );
				}

				if ( 0==strName.find("Channel") )
				{
					doc.AddChildAttrib(	ADSL_CHANEL_ATTR,  strVal.c_str() );
				}

				if ( 0==strName.find("Trellis") )
				{
					doc.AddChildAttrib(	ADSL_TRELLIS_ATTR,  strVal.c_str() );
				}

				if ( 0==strName.find("Line Status") )
				{
					doc.AddChildAttrib(	ADSL_LINE_STATUS_ATTR,  strVal.c_str() );
				}

				if ( 0==strName.find("Training Status") )
				{
					doc.AddChildAttrib(	ADSL_TRAINING_STATUS_ATTR,  strVal.c_str() );
				}

				if ( 0==strName.find("SNR (dB)") )
				{
					if (2==CLogicManager::StringToDigit(strVal, lVals, 2 ) )
					{
						doc.AddChildAttrib(	ADSL_D_SNR_ATTR,  lVals[0] );
						doc.AddChildAttrib( ADSL_U_SNR_ATTR,  lVals[1] );
					}
				}

				if ( 0==strName.find("Attn(dB)") )
				{
					if (2==CLogicManager::StringToDigit(strVal, lVals, 2 ) )
					{
						doc.AddChildAttrib( ADSL_D_ATTN_ATTR,  lVals[0] );
						doc.AddChildAttrib( ADSL_U_ATTN_ATTR,  lVals[1] );
					}
				}

				if ( 0==strName.find("Rate (Kbps)") )
				{
					if (2==CLogicManager::StringToDigit(strVal, lVals, 2 ) )
					{
						doc.AddChildAttrib( ADSL_D_CONRATE_ATTR,  lVals[0] );
						doc.AddChildAttrib( ADSL_U_CONRATE_ATTR,  lVals[1] );
					}
				}

				if ( 0==strName.find("Max(Kbps)") )
				{
					if (2==CLogicManager::StringToDigit(strVal, lVals, 2 ) )
					{
						doc.AddChildAttrib( ADSL_D_MAXRATE_ATTR,  lVals[0] );
						doc.AddChildAttrib( ADSL_U_MAXRATE_ATTR,  lVals[1] );
					}
				}

				if ( 0==strName.find("Rate (Kbps)") )
				{
					if (2==CLogicManager::StringToDigit(strVal, lVals, 2 ) )
					{
						doc.AddChildAttrib( ADSL_D_CONRATE_ATTR,  lVals[0] );
						doc.AddChildAttrib( ADSL_U_CONRATE_ATTR,  lVals[1] );
					}
				}

				if ( 0==strName.find("Pwr(dBm)") )
				{
					if (2==CLogicManager::StringToDigit(strVal, lVals, 2 ) )
					{
						doc.AddChildAttrib( ADSL_D_PWR_ATTR,  lVals[0] );
						doc.AddChildAttrib( ADSL_U_PWR_ATTR,  lVals[1] );
					}
				}

				if ( 0==strName.find("Status") )
				{
					doc.AddChildAttrib( ADSL_ISUP_ATTR, strVal.find("Idle")==std::string::npos?"true":"false" );
				}

				if ( 0==strName.find("HEC") )
				{
					if (2==CLogicManager::StringToDigit(strVal, lVals, 2 ) )
					{
						doc.AddChildAttrib( ADSL_D_HEC_ERR_ATTR,  lVals[0] );
						doc.AddChildAttrib( ADSL_U_HEC_ERR_ATTR,  lVals[1] );
					}
				}

				if ( 0==strName.find("OCD") )
				{
					if (2==CLogicManager::StringToDigit(strVal, lVals, 2 ) )
					{
						doc.AddChildAttrib( ADSL_D_OCD_ERR_ATTR,  lVals[0] );
						doc.AddChildAttrib( ADSL_U_OCD_ERR_ATTR,  lVals[1] );
					}
				}

				if ( 0==strName.find("LCD") )
				{
					if (2==CLogicManager::StringToDigit(strVal, lVals, 2 ) )
					{
						doc.AddChildAttrib( ADSL_D_LCD_ERR_ATTR,  lVals[0] );
						doc.AddChildAttrib( ADSL_U_LCD_ERR_ATTR,  lVals[1] );
					}
				}

				if ( 0==strName.find("SFErr") )
				{
					if (2==CLogicManager::StringToDigit(strVal, lVals, 2 ) )
					{
						doc.AddChildAttrib( ADSL_D_SFE_ERR_ATTR,  lVals[0] );
						doc.AddChildAttrib( ADSL_U_SFE_ERR_ATTR,  lVals[1] );
					}
				}

				if ( strName == "SF" ) 
				{
					if (2==CLogicManager::StringToDigit(strVal, lVals, 2 ) )
					{
						doc.AddChildAttrib( ADSL_D_SF_ERR_ATTR,  lVals[0] );
						doc.AddChildAttrib( ADSL_U_SF_ERR_ATTR,  lVals[1] );
					}
				}
			}
		}

		NAMEDVALS lstErr;
		CLogicManager::GetSection(strData, "Total time", lstErr);

		for( size_t i=0;i<lstErr.size();i++ )
		{
			std::string strName = lstErr[i].strLabel;
			size_t stVal  = atol( lstErr[i].strVal.c_str());

			if ( 0==strName.find("CRC") )
			{
				doc.AddChildAttrib( ADSL_CRC_ERR_ATTR,  stVal );
			}

			if ( 0==strName.find("ES") )
			{
				doc.AddChildAttrib( ADSL_ESEC_ERR_ATTR,  stVal );
			}

			if ( 0==strName.find("LOS") )
			{
				doc.AddChildAttrib( ADSL_LOS_ERR_ATTR,  stVal );
			}

			if ( 0==strName.find("LOF") )
			{
				doc.AddChildAttrib( ADSL_LOF_ERR_ATTR,  stVal );
			}
		}

		//TODO: calculate dynamic
		RTSTRING strData = m_link->SendCmd(m_strCMD+wxT(" info --Bits"), 10000 );
		if ( !strData.empty() )
		{

			NAMEDVALS lst;

			if ( CLogicManager::ExtractPairs(" ", strData, lst, "Tone number") )
			{
				doc.AddChildElem( ADSL_BITS_U_TAG  );
				doc.IntoElem();

				for( size_t i=0;i<std::min<size_t>(DOWN_STREAM_BIN_STARTS, lst.size());i++ )
				{
					std::string strName = lst[i].strLabel;
					std::string strVal  = lst[i].strVal;

					if ( atol(strName.c_str()) )
					{
						doc.AddChildElem( ADSL_TONE_ATTR  );
						doc.AddChildAttrib( ADSL_TONE_ID_ATTR, strName.c_str()  );
						doc.AddChildAttrib( ADSL_TONE_VAL_ATTR, strVal.c_str()  );
					}
				}

				doc.OutOfElem();

				doc.AddChildElem( ADSL_BITS_D_TAG  );
				doc.IntoElem();

				for( size_t i=DOWN_STREAM_BIN_STARTS;i<lst.size();i++ )
				{
					std::string strName = lst[i].strLabel;
					std::string strVal  = lst[i].strVal;

					if ( atol(strName.c_str()) )
					{
						doc.AddChildElem( ADSL_TONE_ATTR  );
						doc.AddChildAttrib( ADSL_TONE_ID_ATTR, strName.c_str()  );
						doc.AddChildAttrib( ADSL_TONE_VAL_ATTR, strVal.c_str()  );
					}
				}
				doc.OutOfElem();
			}
		}

		strData = m_link->SendCmd(m_strCMD+wxT(" info --SNR"), 10000 );

		if ( !strData.empty() )
		{
			NAMEDVALS lst;

			if ( CLogicManager::ExtractPairs(" ", strData, lst, "Tone number") )
			{
				doc.AddChildElem( ADSL_SNR1_BIT_TAG  );
				doc.IntoElem();

				for( size_t i=0;i<lst.size();i++ )
				{
					std::string strName = lst[i].strLabel;
					std::string strVal  = lst[i].strVal;

					if ( atol(strName.c_str()) )
					{
						doc.AddChildElem( ADSL_TONE_ATTR  );
						doc.AddChildAttrib( ADSL_TONE_ID_ATTR, strName.c_str()  );

						//TODO:Draw minus
						if ( atol(strVal.c_str())<0 )
						{
							doc.AddChildAttrib( ADSL_TONE_VAL_ATTR,"0" );
						}
						else
						{
							doc.AddChildAttrib( ADSL_TONE_VAL_ATTR, strVal.c_str()  );
						}
					}
				}

				doc.OutOfElem();
			}
		}

		strRet = doc.GetDoc();
	}

	return strRet;
}

//adsl configure [--mod <a|d|l|t|2|p|e|m>] [--lpair <(i)nner|(o)uter>]
//[--trellis <on|off>] [--snr <snrQ4>] [--bitswap <on|off>]
std::string	 CADSLProgModem::GetCaps()
{
	CMarkupSTL	doc;
	doc.AddElem( ADSL_CAPS );

	doc.AddAttrib( GLOBAL_NAME_ATR,		"Generic ADSL" );

	//Modes
	doc.AddChildElem( XMLUI_BLOCK_TAG );
	doc.AddChildAttrib( XMLUI_BLOCK_NAME_ATR, (const char*)wxString( _("Modulation:") ).utf8_str() );
	//doc.AddChildAttrib( XMLUI_VAL_CMD_ATTR,		"configure --mod" );
	//doc.AddChildAttrib( XMLUI_VAL_CMD_SEP_ATTR,	"|" );
	doc.AddChildAttrib( XMLUI_ELEMENT_BLOCK_ID_ATTR,	"mode" );

	doc.IntoElem();
	for(size_t i=0;i<sizeof(gModes)/sizeof( gModes[0]);++i)
	{
		MODESELECTOR& mode = gModes[i];
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_CHECK );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	mode.szModeID );
		doc.AddChildAttrib( XMLUI_ELEMENT_LOOKUP_ID_ATTR,	mode.strUserName.ToAscii() );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	mode.strUserName.utf8_str() );
		doc.AddChildAttrib( XMLUI_MODE_DESC_ATTR,	mode.strDescription.utf8_str() );
		doc.AddChildAttrib( XMLUI_VAL_ATTR,			ADSL_MODE_ATTR ); //????
		doc.AddChildAttrib( XMLUI_VAL_TO_ATTR,		"--mod %s");
	}

	//Apply
	doc.AddChildElem( XMLUI_UI_TAG );
	doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	"apply_mode" );
	doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_BUTTON );
	doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString(_("Apply") ).utf8_str() 	);
	doc.AddChildAttrib( XMLUI_UI_ATTR_BTN_APP_BLOCK, "mode");

	doc.OutOfElem();

	//Actions
	doc.AddChildElem( XMLUI_BLOCK_TAG );
	doc.AddChildAttrib( XMLUI_BLOCK_NAME_ATR, wxString( _("Actions:") ).utf8_str() );
	doc.IntoElem();
	{
		//Start
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	"start" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_BUTTON );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString(_("Connect") ).utf8_str() 	);
		doc.AddChildAttrib( XMLUI_VAL_TO_ATTR,		"connection --up");

		//Stop
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	"stop" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_BUTTON );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	 wxString(_("Disconnect") ).utf8_str() 	);
		doc.AddChildAttrib( XMLUI_VAL_TO_ATTR,		"connection --down");

		//Apply
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	"apply_all" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_BUTTON );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString(_("Apply All") ).utf8_str() );
		doc.AddChildAttrib( XMLUI_UI_ATTR_BTN_APP_BLOCK, "all");
	}
	doc.OutOfElem();

	//Settings
	doc.AddChildElem( XMLUI_BLOCK_TAG );
	doc.AddChildAttrib( XMLUI_BLOCK_NAME_ATR, wxString( _("Capability:") ).utf8_str() );
	doc.AddChildAttrib( XMLUI_ELEMENT_BLOCK_ID_ATTR,	"settings" );
	doc.IntoElem();
	{
		//Bitswap
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	"Bitswap" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_CHECK );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("Bitswap") ).utf8_str()	);
		doc.AddChildAttrib( XMLUI_VAL_TO_ON_ATTR,	"--bitswap on");
		doc.AddChildAttrib( XMLUI_VAL_TO_OFF_ATTR,	"--bitswap off");

		//SRA
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	"sra" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_CHECK );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("SRA (ADSL2 only)") ).utf8_str()	);
		doc.AddChildAttrib( XMLUI_VAL_TO_ON_ATTR,	"--sra on");
		doc.AddChildAttrib( XMLUI_VAL_TO_OFF_ATTR,	"--sra off");

		//trellis 
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	"Trellis" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_CHECK );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("Trellis") ).utf8_str()		);
		doc.AddChildAttrib( XMLUI_VAL_TO_ON_ATTR,	"--trellis on");
		doc.AddChildAttrib( XMLUI_VAL_TO_OFF_ATTR,	"--trellis off");

		//SNR
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"snr" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_INPUT );
		doc.AddChildAttrib( XMLUI_UI_ATTR_INPUT_TYPE, XMLUI_UI_ATTR_INPUT_TYPE_D );
		doc.AddChildAttrib( XMLUI_UI_ATTR_INPUT_FROM,1    );
		doc.AddChildAttrib( XMLUI_UI_ATTR_INPUT_TO,  9999 );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("SNR level in %") ).utf8_str()		);
		doc.AddChildAttrib( XMLUI_VAL_TO_ATTR,		"--snr %d");

		//sesdrop
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"sesdrop" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_CHECK );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("Drop session") ).utf8_str()		);
		doc.AddChildAttrib( XMLUI_VAL_TO_ON_ATTR,	"--sesdrop on");
		doc.AddChildAttrib( XMLUI_VAL_TO_OFF_ATTR,	"--sesdrop off");

		//Apply
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"apply_settings" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_BUTTON );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString(_("Apply") ).utf8_str() 	);
		doc.AddChildAttrib( XMLUI_UI_ATTR_BTN_APP_BLOCK, "settings");
	}
	doc.OutOfElem();

	return doc.GetDoc();
}

void CADSLProgModem::SetUpModem(const std::string& strModemSettings)
{
	CMarkupSTL	doc;

	if ( doc.SetDoc(strModemSettings.c_str()) )
	{
		doc.FindElem();

		if ( doc.GetAttrib(XMLGUI_EXEC_ATTR_STARTUP)=="true" )
		{
			ExecuteXML(strModemSettings);
		}
	}
}

/*
<exec>
<exec_item id="l">--mod l</exec_item>
<exec_item id="bitswap">--bitswap off</exec_item>
<exec_item id="sra">--sra off</exec_item>
<exec_item id="trellis">--trellis off</exec_item>
<exec_item id="snr">--snr 0</exec_item>
<exec_item id="sesdrop">--sesdrop off</exec_item>
</exec>
*/

/*
adsl configure [--mod <a|d|l|t|2|p|e|m>] [--lpair <(i)nner|(o)uter>]
[--trellis <on|off>] [--snr <snrQ4>] [--bitswap <on|off>] [--sesdrop<on|off>]
[--sra <on|off>][--CoMinMgn <on|off>] [--i24k <on|off>] [--phyReXmt  <on|off>]
*/

bool CADSLProgModem::ExecuteXML(const std::string& strModemSettings)
{
	bool bRes(false);

	CMarkupSTL	doc;

	if ( doc.SetDoc(strModemSettings.c_str()) )
	{
		while( doc.FindChildElem(XMLGUI_EXEC_ITEM_EL) )
		{
			std::string strExec = doc.GetChildData();

			if ( !strExec.empty() )
			{
				bRes = ExecCMD(strExec);
			}
		}
	}

	return bRes;
}

bool CADSLProgModem::ExecCMD(const std::string& strExec)
{
	bool bRes(false);

	wxString strCMD;
	strCMD+=m_strCMD;
	strCMD+=wxT(" ");

	if ( strExec[0]=='-' )
	{
		strCMD+=wxT("configure ");
	}
		
	strCMD+=wxString::FromAscii( strExec.c_str() );
	std::string strData = m_link->SendCmd(strCMD, 200 );

	if ( std::string::npos==strData.find("invalid") &&
		 std::string::npos==strData.find("too many") )
	{
		bRes = true;
	}

	return bRes;
}